<?php

namespace yunj\builder;

use yunj\Config;
use yunj\Validate;
use yunj\enum\BuilderType;
use yunj\control\YunjControl;

final class YunjTable extends Builder {

    protected $type = BuilderType::TABLE;

    protected $builderValidateClass = "\\yunj\\validate\\YunjTable";

    protected $scriptFileList = ["/static/yunj/js/table.min.js?v=" . YUNJ_VERSION];

    protected $options = ["state", "url", "page", "limit", "limits", "filter", "filterValidate"
        , "toolbar", "defaultToolbar", "import", "cols", "count", "items", "event"];

    // 默认状态
    // TODO 弃用
    const DEF_STATE = 'default';

    // 导出限制，每次2000条
    const EXPORT_LIMIT = 2000;

    /**
     * 状态栏（默认选中第一项）
     * 'state'=>[
     *      11=>'正常',
     *      22=>'回收站'
     *  ]
     * @var array<key,title>
     */
    private $state = [];

    /**
     * 异步数据接口地址（默认当前地址）
     * "url"=>"http://xxx.com/xxx.html"
     * @var string
     */
    private $url;

    /**
     * 是否开启分页（默认true）
     * 示例：state没设置时 bool
     * "page"=>true
     * 示例：state设置时 array<state-key,bool>
     * "page"=>[
     *      11=>true,   // key为状态栏key值
     *      22=>false
     * ]
     * @var bool|array<state-key,bool>
     */
    private $page = true;

    /**
     * 每页显示数据条数（默认20）。在page开启时有效
     * 示例：state没设置时 int
     * "limit"=>20
     * 示例：state设置时 array<state-key,int>
     * "limit"=>[
     *      11=>20,     // key为状态栏key值
     *      22=>50
     * ]
     * @var int|array<state-key,int>
     */
    private $limit = 20;

    /**
     * 每页条数的选择项（默认[10,20,30,40,50,60,70,80,90]）。在page开启时有效
     * 示例：state没设置时 array<int>
     * "limits"=>[10,20,30,40,50,60,70,80,90]
     * 示例：state设置时 array<state-key,array<int>>
     * 'limits'=>[
     *      11=>[10,30,50,70,90],   // key为状态栏key值
     *      22=>[20,40,60,80,90]
     *  ]
     * @var array<int>|array<state-key,array<int>>
     */
    private $limits = [10, 20, 30, 40, 50, 60, 70, 80, 90];

    /**
     * 筛选表单（默认空）
     * 示例：state没设置时 array<field-key,field-args>
     * 'filter'=>[
     *      'field_key'=>[
     *          'type'=>'text',         //（可选，默认text）
     *          'title'=>'field_label',
     *          'placeholder'=>'',      //（可选）
     *          'value'=>'',            //默认值（可选）
     *      ],...
     *  ]
     * 示例：state设置时 array<state-key,array<field-key,field-args>>
     * 'filter'=>[
     *      11=>[],
     *      22=>[
     *          'field_key'=>[
     *              'type'=>'text',         //（可选，默认text）
     *              'title'=>'field_label',
     *              'placeholder'=>'',      //（可选）
     *              'value'=>'',            //默认值（可选）
     *          ],...
     *      ]
     *  ]
     * @var array<field-key,field-args>|array<state-key,array<field-key,field-args>>
     */
    private $filter;

    /**
     * 筛选表单验证的回调方法
     * @var callable
     */
    private $filterValidateCallback;

    /**
     * 头部工具栏（默认空）
     * 示例：state没设置时 array<event-key,event-args>
     * 'toolbar'=>[
     *      "add"=>[
     *          'type'=>'open_popup',
     *          'title'=>'添加',
     *          'icon'=>'layui-icon layui-icon-add-circle',
     *          'url'=>url('add')
     *      ],
     *      11=>[
     *          'type'=>'asyncEvent',
     *          'title'=>'还原',
     *          'dropdown'=>true
     *      ],
     *      33=>[
     *          'type'=>'asyncEvent',
     *          'title'=>'永久删除',
     *          'dropdown'=>true
     *      ],...
     * ]
     *
     * 示例：state设置时 array<state-key,array<event-key,event-args>>
     * 'toolbar'=>[
     *      11=>[],
     *      22=>[
     *          "add"=>[
     *              'type'=>'open_popup',
     *              'title'=>'添加',
     *              'icon'=>'layui-icon layui-icon-add-circle',
     *              'url'=>url('add')
     *          ],
     *          11=>[
     *              'type'=>'asyncEvent',
     *              'title'=>'还原',
     *              'dropdown'=>true
     *          ],
     *          33=>[
     *              'type'=>'asyncEvent',
     *              'title'=>'永久删除',
     *              'dropdown'=>true
     *          ],
     *          ...
     *      ]
     * ]
     * @var array<event-key,event-args>|array<state-key,array<event-key,event-args>>
     */
    private $toolbar;

    /**
     * 头部右侧工具栏（默认空）
     * 示例：state没设置时 array<string>
     * 'defaultToolbar'=>[filter','export','print']   // 可选值有：filter 筛选列的显示隐藏、export 数据导出、print 打印当前显示的表格数据
     *
     * 示例：state设置时 array<state-key,array<string>>
     * 'defaultToolbar'=>[
     *      11=>[],
     *      22=>[
     *          'filter','export','print'   // 可选值有：filter 筛选列的显示隐藏、export 数据导出、print 打印当前显示的表格数据
     *      ]
     * ]
     * @var array<string>|array<state-key,array<string>>
     */
    private $defaultToolbar;

    /**
     * 数据导入页面地址
     * 示例：state没设置时 string
     * "import"=>url("...")
     *
     * 示例：state设置时 array<state-key,string>
     * 'import'=>[
     *      11=>url("..."),
     *      22=>url("...")
     * ]
     * @var string|array<state-key,string>
     */
    private $import;

    /**
     * 必须：表头
     * 示例：state没设置时 array<col-key,col-args>
     * 'cols'=>[
     *      'id'=>['type'=>'checkbox'],
     *      'name_cn'=>['title'=>'中文姓名'],
     *      ...
     * ]
     *
     * 示例：state设置时 array<state-key,array<col-key,col-args>>
     * 'cols'=>[
     *      11=>[],
     *      22=>[
     *          'id'=>['type'=>'checkbox'],
     *          'name_cn'=>['title'=>'中文姓名'],
     *          ...
     *      ]
     * ]
     * @var array<col-key,col-args>|array<state-key,array<col-key,col-args>>
     */
    private $cols;

    /**
     * 获取数量回调方法
     * @var callable
     */
    private $countCallback;

    /**
     * 获取数据项回调方法
     * @var callable
     */
    private $itemsCallback;

    /**
     * 事件触发回调方法
     * @var callable
     */
    private $eventCallback;

    protected function setAttr(array $args = []): void {
        parent::setAttr($args);
        $this->config = Config::get('table.', []);
    }

    /**
     * Notes: 状态栏
     * Author: Uncle-L
     * Date: 2020/5/12
     * Time: 16:20
     * @param callable|array|string|number $code
     * callable:
     *      function(){
     *          return [11=>"正常",22=>"回收站"];
     *      }
     * array:
     *      [11=>"正常",22=>"回收站"];
     * @param string $title
     * @return YunjTable
     */
    public function state($code, string $title = '') {
        if ($this->existError()) return $this;
        if (!is_callable($code)) {
            $callable = function () use ($code, $title) {
                return is_array($code) ? $code : [$code => $title];
            };
        } else {
            $callable = $code;
        }
        $this->setOptionCallbale('state', $callable);
        return $this;
    }

    /**
     * Notes: 执行状态栏回调
     * Author: Uncle-L
     * Date: 2021/3/29
     * Time: 10:42
     * @param callable $callable
     * @return YunjTable
     */
    protected function exec_state_callable(callable $callable) {
        $state = $this->state ? $this->state : [];
        $result = call_user_func($callable);
        if (!is_array($result)) return $this->error("YunjTable [state] 需返回有效数组");
        $this->state = $result + $state;
        return $this;
    }

    /**
     * Notes: 判断是否设置state
     * Author: Uncle-L
     * Date: 2022/3/8
     * Time: 17:30
     * @return bool
     */
    private function isSetState(): bool {
        $this->execOptionsCallbale("state");
        return $this->state && count($this->state) > 0;
    }

    /**
     * Notes: 请求地址
     * Author: Uncle-L
     * Date: 2021/3/29
     * Time: 10:41
     * @param callable|string $url
     * callable:
     *      function(){
     *          return "xxxx.com/xxx.html";
     *      }
     * string:
     *      xxxx.com/xxx.html
     * @return YunjTable
     */
    public function url($url) {
        if ($this->existError()) return $this;
        if (!is_callable($url)) {
            $callable = function () use ($url) {
                return $url;
            };
        } else {
            $callable = $url;
        }
        $this->setOptionCallbale('url', $callable);
        return $this;
    }

    /**
     * Notes: 执行请求地址回调
     * Author: Uncle-L
     * Date: 2021/10/8
     * Time: 18:33
     * @param callable $callable
     * @return YunjTable
     */
    protected function exec_url_callable(callable $callable) {
        if ($this->existError()) return $this;
        $url = $callable();
        if (!is_string($url)) return $this->error("YunjTable [url] 需返回有效地址");
        $this->url = $url;
        return $this;
    }

    /**
     * Notes: 分页开启
     * Author: Uncle-L
     * Date: 2021/3/29
     * Time: 10:54
     * @param callable|bool $page
     * callable:
     *      function($state){
     *          return true;
     *      }
     * bool:
     *      true
     * @return YunjTable
     */
    public function page($page) {
        if ($this->existError()) return $this;
        if (!is_callable($page)) {
            $callable = function () use ($page) {
                return $page;
            };
        } else {
            $callable = $page;
        }
        $this->setOptionCallbale('page', $callable);
        return $this;
    }

    /**
     * Notes: 执行分页开启回调
     * Author: Uncle-L
     * Date: 2021/3/29
     * Time: 10:10
     * @param callable $callable
     * @return YunjTable
     */
    protected function exec_page_callable(callable $callable) {
        if ($this->existError()) return $this;
        $states = $this->state;
        if ($states) {
            $page = $this->page ?: [];
            foreach ($states as $state => $title) {
                $statePage = $callable($state);
                if (!is_bool($statePage)) return $this->error("YunjTable [page] 需返回布尔结果");
                $page[$state] = $statePage;
            }
        } else {
            $page = $callable(null);
            if (!is_bool($page)) return $this->error("YunjTable [page] 需返回布尔结果");
        }
        $this->page = $page;
        return $this;
    }

    /**
     * Notes: 每页显示条数
     * Author: Uncle-L
     * Date: 2021/3/29
     * Time: 10:55
     * @param callable|int $limit
     * callable:
     *      function($state){
     *          return true;
     *      }
     * int:
     *      20
     * @return YunjTable
     */
    public function limit($limit) {
        if ($this->existError()) return $this;
        if (!is_callable($limit)) {
            $callable = function () use ($limit) {
                return $limit;
            };
        } else {
            $callable = $limit;
        }
        $this->setOptionCallbale('limit', $callable);
        return $this;
    }

    /**
     * Notes: 执行每页显示条数回调
     * Author: Uncle-L
     * Date: 2021/3/29
     * Time: 10:10
     * @param callable $callable
     * @return YunjTable
     */
    protected function exec_limit_callable(callable $callable) {
        if ($this->existError()) return $this;
        if (!$this->page) return $this;
        $states = $this->state;
        if ($states) {
            $limit = $this->limit ?: [];
            foreach ($states as $state => $title) {
                $stateLimit = $callable($state);
                if (!is_positive_int($stateLimit)) return $this->error("YunjTable [limit] 需返回正整数结果");
                $limit[$state] = $stateLimit;
            }
        } else {
            $limit = $callable(null);
            if (!is_positive_int($limit)) return $this->error("YunjTable [limit] 需返回正整数结果");
        }
        $this->limit = $limit;
        return $this;
    }

    /**
     * Notes: 每页显示条数的选择项
     * Author: Uncle-L
     * Date: 2021/3/29
     * Time: 10:55
     * @param callable|array<int>|...int $limits
     * callable:
     *      function($state){
     *          return [10,20,30];
     *      }
     * array:
     *      [10,20,30]
     * @return YunjTable
     */
    public function limits(...$limits) {
        if ($this->existError()) return $this;
        if (!$limits) return $this;
        $limit0 = $limits[0];
        if (is_callable($limit0)) {
            $callable = $limit0;
        } elseif (is_array($limit0)) {
            $callable = function () use ($limit0) {
                return $limit0;
            };
        } else {
            $callable = function () use ($limits) {
                return $limits;
            };
        }
        $this->setOptionCallbale('limits', $callable);
        return $this;
    }

    /**
     * Notes: 执行每页显示条数的选择项回调
     * Author: Uncle-L
     * Date: 2021/3/29
     * Time: 10:10
     * @param callable $callable
     * @return YunjTable
     */
    protected function exec_limits_callable(callable $callable) {
        if ($this->existError()) return $this;
        if (!$this->page) return $this;
        $states = $this->state;
        if ($states) {
            $limits = $this->limits ?: [];
            foreach ($this->state as $state => $title) {
                $stateLimits = $callable($state);
                if (!is_array($stateLimits)) return $this->error("YunjTable [limits] 需返回正整数数组");
                $limits[$state] = $stateLimits;
            }
        } else {
            $limits = $callable(null);
            if (!is_array($limits)) return $this->error("YunjTable [limits] 需返回正整数数组");
        }
        $this->limits = $limits;
        return $this;
    }

    /**
     * Notes: 筛选表单
     * Author: Uncle-L
     * Date: 2021/3/29
     * Time: 10:58
     * @param callable|array<field-key,field-args> $filter
     * @return YunjTable
     */
    public function filter($filter) {
        if ($this->existError()) return $this;
        if (!is_callable($filter)) {
            $callable = function () use ($filter) {
                return $filter;
            };
        } else {
            $callable = $filter;
        }
        $this->setOptionCallbale('filter', $callable);
        return $this;
    }

    /**
     * Notes: 执行筛选表单回调
     * Author: Uncle-L
     * Date: 2021/3/29
     * Time: 10:10
     * @param callable $callable
     * @return YunjTable
     */
    protected function exec_filter_callable(callable $callable) {
        if ($this->existError()) return $this;
        $states = $this->state;
        $filter = $this->filter ?: [];
        if ($states) {
            foreach ($this->state as $state => $title) {
                $stateFilter = $callable($state);
                if (!is_array($stateFilter)) return $this->error("YunjTable [filter] 需返回有效数组");
                $filter[$state] = $filter[$state] ?? [];
                $this->handleFilterState($filter[$state], $stateFilter);
            }
        } else {
            $_filter = $callable(null);
            if (!is_array($_filter)) return $this->error("YunjTable [filter] 需返回有效数组");
            $this->handleFilterState($filter, $_filter);
        }
        $this->filter = $filter;
        return $this;
    }

    /**
     * Notes: 判断是否设置filter
     * Author: Uncle-L
     * Date: 2022/3/8
     * Time: 17:30
     * @return bool
     */
    private function isSetFilter(): bool {
        $this->execOptionsCallbale("filter");
        return !!$this->filter;
    }

    /**
     * Notes: 筛选表单的验证器
     * Author: Uncle-L
     * Date: 2021/3/29
     * Time: 10:05
     * @param callable|string<Validate-class-name> $filterValidate
     * callable:
     *      function(){
     *          // 返回验证器全限定类名
     *          return \\demo\\validate\\TestValidate::class;
     *          // 返回验证器示例
     *          return new TestValidate();
     *      }
     * string:
     *      \\demo\\validate\\TestValidate::class
     * @return YunjTable
     */
    public function filterValidate($filterValidate) {
        if ($this->existError()) return $this;
        if (!is_callable($filterValidate)) {
            $callable = function () use ($filterValidate) {
                return $filterValidate;
            };
        } else {
            $callable = $filterValidate;
        }
        $this->filterValidateCallback = $callable;
        return $this;
    }

    /**
     * Notes: 处理状态栏对应的筛选表单字段
     * Author: Uncle-L
     * Date: 2021/3/29
     * Time: 10:59
     * @param array $filterState
     * @param array $fields
     * @return YunjTable
     */
    protected function handleFilterState(array &$filterState, array $fields) {
        foreach ($fields as $key => $field) {
            if (!$field || !is_array($field)) return $this->error("YunjTable [filter] 返回参数[{$key}]异常");
            $filterState[$key] = YunjControl::field($key, $field)->args();
        }
        return $this;
    }

    /**
     * Notes: 头部工具栏
     * Author: Uncle-L
     * Date: 2021/3/29
     * Time: 11:00
     * @param callable|array<event-key,event-args> $toolbar
     * @return YunjTable
     */
    public function toolbar($toolbar) {
        if ($this->existError()) return $this;
        if (!is_callable($toolbar)) {
            $callable = function () use ($toolbar) {
                return $toolbar;
            };
        } else {
            $callable = $toolbar;
        }
        $this->setOptionCallbale('toolbar', $callable);
        return $this;
    }

    /**
     * Notes: 执行头部工具栏回调
     * Author: Uncle-L
     * Date: 2021/3/29
     * Time: 10:10
     * @param callable $callable
     * @return YunjTable
     */
    protected function exec_toolbar_callable(callable $callable) {
        if ($this->existError()) return $this;
        $states = $this->state;
        $toolbar = $this->toolbar ? $this->toolbar : [];
        if ($states) {
            foreach ($states as $state => $title) {
                $stateToolbar = $callable($state);
                if (!is_array($stateToolbar)) return $this->error("YunjTable [toolbar] 需返回有效数组");
                $toolbar[$state] = isset($toolbar[$state]) ? $toolbar[$state] : [];
                foreach ($stateToolbar as $event => $args) {
                    $toolbar[$state][$event] = YunjControl::toolbar($event, $args)->args();
                }
            }
        } else {
            $_toobar = $callable(null);
            if (!is_array($_toobar)) return $this->error("YunjTable [toolbar] 需返回有效数组");
            foreach ($_toobar as $event => $args) {
                $toolbar[$event] = YunjControl::toolbar($event, $args)->args();
            }
        }
        $this->toolbar = $toolbar;
        return $this;
    }

    /**
     * Notes: 头部右侧工具栏
     * Author: Uncle-L
     * Date: 2021/3/29
     * Time: 11:02
     * @param callable|array<string>|...string $defaultToolbar
     * callable:
     *      function($state){
     *          return ["filter","print"];
     *      }
     * array<string>:
     *      ["filter","print"]
     * ...string:
     *      "filter","print"
     * @return YunjTable
     */
    public function defaultToolbar(...$defaultToolbar) {
        if ($this->existError()) return $this;

        if (!$defaultToolbar) return $this;
        $defaultToolbar0 = $defaultToolbar[0];
        if (is_callable($defaultToolbar0)) {
            $callable = $defaultToolbar0;
        } elseif (is_array($defaultToolbar0)) {
            $callable = function () use ($defaultToolbar0) {
                return $defaultToolbar0;
            };
        } else {
            $callable = function () use ($defaultToolbar) {
                return $defaultToolbar;
            };
        }
        $this->setOptionCallbale('defaultToolbar', $callable);
        return $this;
    }

    /**
     * Notes: 执行头部右侧工具栏回调
     * Author: Uncle-L
     * Date: 2021/3/29
     * Time: 10:10
     * @param callable $callable
     * @return YunjTable
     */
    protected function exec_defaultToolbar_callable(callable $callable) {
        if ($this->existError()) return $this;
        $states = $this->state;
        $defaultToolbar = $this->defaultToolbar ?: [];
        if ($states) {
            foreach ($states as $state => $title) {
                $stateDefaultToolbar = $callable($state);
                if (!is_array($stateDefaultToolbar)) return $this->error("YunjTable [defaultToolbar] 需返回有效数组");
                $defaultToolbar[$state] = $defaultToolbar[$state] ?? [];
                if (!array_in($stateDefaultToolbar, $this->config['defaultToolbar']))
                    return $this->error("YunjTable [defaultToolbar] 返回值不在有效范围=>" . json_encode($this->config['defaultToolbar']));
                $defaultToolbar[$state] = array_keys(array_flip($defaultToolbar[$state]) + array_flip($stateDefaultToolbar));
            }
        } else {
            $_defaultToolbar = $callable(null);
            if (!is_array($_defaultToolbar)) return $this->error("YunjTable [defaultToolbar] 需返回有效数组");
            if (!array_in($_defaultToolbar, $this->config['defaultToolbar']))
                return $this->error("YunjTable [defaultToolbar] 返回值不在有效范围=>" . json_encode($this->config['defaultToolbar']));
            $defaultToolbar = array_keys(array_flip($defaultToolbar) + array_flip($_defaultToolbar));
        }
        $this->defaultToolbar = $defaultToolbar;
        return $this;
    }

    /**
     * Notes: 数据导入页面地址
     * Author: Uncle-L
     * Date: 2021/3/29
     * Time: 11:05
     * @param callable|string $import
     * callable:
     *      function($state){
     *          return "xxxx.com/xxx.html";
     *      }
     * string:
     *      xxxx.com/xxx.html
     * @return YunjTable
     */
    public function import($import) {
        if ($this->existError()) return $this;
        if (!is_callable($import)) {
            $callable = function () use ($import) {
                return $import;
            };
        } else {
            $callable = $import;
        }
        $this->setOptionCallbale('import', $callable);
        return $this;
    }

    /**
     * Notes: 执行数据导入页面地址回调
     * Author: Uncle-L
     * Date: 2021/3/29
     * Time: 10:10
     * @param callable $callable
     * @return YunjTable
     */
    protected function exec_import_callable(callable $callable) {
        if ($this->existError()) return $this;
        $states = $this->state;
        if ($states) {
            $import = $this->import ? $this->import : [];
            foreach ($states as $state => $title) {
                $stateImport = $callable($state);
                if (!is_string($stateImport)) return $this->error("YunjTable [import] 需返回有效地址");
                $import[$state] = $stateImport;
            }
        } else {
            $import = $callable(null);
            if (!is_string($import)) return $this->error("YunjTable [import] 需返回有效地址");
        }
        $this->import = $import;
        return $this;
    }

    /**
     * Notes: 表头
     * Author: Uncle-L
     * Date: 2021/3/29
     * Time: 11:03
     * @param callable|array<col-key,col-args> $cols
     * @return YunjTable
     */
    public function cols($cols) {
        if ($this->existError()) return $this;
        if (!is_callable($cols)) {
            $callable = function () use ($cols) {
                return $cols;
            };
        } else {
            $callable = $cols;
        }
        $this->setOptionCallbale('cols', $callable);
        return $this;
    }

    /**
     * Notes: 执行表头回调
     * Author: Uncle-L
     * Date: 2021/3/29
     * Time: 10:10
     * @param callable $callable
     * @return YunjTable
     */
    protected function exec_cols_callable(callable $callable) {
        if ($this->existError()) return $this;
        $states = $this->state;
        $cols = $this->cols ?: [];
        if ($states) {
            foreach ($states as $state => $title) {
                $stateCols = $callable($state);
                if (!is_array($stateCols)) return $this->error("YunjTable [cols] 需返回有效数组");
                $cols[$state] = $cols[$state] ?? [];
                foreach ($stateCols as $key => $args) {
                    $cols[$state][$key] = YunjControl::col($key, $args)->args();
                }
            }
        } else {
            $_cols = $callable(null);
            if (!is_array($_cols)) return $this->error("YunjTable [cols] 需返回有效数组");
            foreach ($_cols as $key => $args) {
                $cols[$key] = YunjControl::col($key, $args)->args();
            }
        }
        $this->cols = $cols;
        return $this;
    }

    /**
     * Notes: 数量获取
     * Author: Uncle-L
     * Date: 2021/3/29
     * Time: 11:04
     * @param callable $callable
     * @return YunjTable
     */
    public function count(callable $callable) {
        if ($this->existError()) return $this;
        $this->countCallback = $callable;
        return $this;
    }

    /**
     * Notes: 数据获取
     * Author: Uncle-L
     * Date: 2021/3/29
     * Time: 11:04
     * @param callable $callable
     * @return YunjTable
     */
    public function items(callable $callable) {
        if ($this->existError()) return $this;
        $this->itemsCallback = $callable;
        return $this;
    }

    /**
     * Notes: 事件处理
     * Author: Uncle-L
     * Date: 2021/3/29
     * Time: 11:04
     * @param callable $callable
     * @return YunjTable
     */
    public function event(callable $callable) {
        if ($this->existError()) return $this;
        $this->eventCallback = $callable;
        return $this;
    }

    public function viewArgs() {
        $args = parent::viewArgs();
        if ($this->state) {
            $args['state'] = $this->state;
        }
        if ($this->filter) {
            $args['filter'] = $this->filter;
        }
        if ($this->url) {
            $args['url'] = $this->url;
        }
        if ($this->cols) {
            $args['cols'] = $this->cols;
            if ($this->page || is_bool($this->page)) {
                $page = $this->page;
                $args['page'] = $page;
                if ($page) {
                    if ($this->limit) $args['limit'] = $this->limit;
                    if ($this->limits) $args['limits'] = $this->limits;
                }
            }
            if ($this->toolbar) {
                $args['toolbar'] = $this->toolbar;
            }
            if ($this->defaultToolbar) {
                $args['defaultToolbar'] = $this->defaultToolbar;
            }
            if ($this->import) {
                $args['import'] = $this->import;
            }
        }
        return $args;
    }

    /**
     * Notes: 异步处理状态数量
     * Author: Uncle-L
     * Date: 2020/7/8
     * Time: 13:37
     * @return array
     */
    protected function async_stateCount(): array {
        $response = [];
        $this->execOptionsCallbale('state,filter');
        $states = $this->state;
        if (!$states) return error_json();
        $filter = $this->filter;
        foreach ($states as $state => $title) {
            $data = [
                "state" => $state,
                "ids" => [],
            ];
            if (isset($filter[$state]) && $filter[$state])
                foreach ($filter[$state] as $k => $field) $data[$k] = "";
            $response[$state] = call_user_func_array($this->countCallback, [$data]);
        }
        return $response;
    }

    /**
     * Notes: 异步处理数量
     * Author: Uncle-L
     * Date: 2020/7/8
     * Time: 13:39
     * @param $data
     * @return mixed
     */
    protected function async_count(array $data) {
        $this->execOptionsCallbale("count");
        if (!$this->countCallback) throw_error_json("YunjTable [count] 未设置");
        $filterData = $data['filter'];
        $this->async_filterValidate($filterData, "count");
        $response = ['count' => call_user_func_array($this->countCallback, [$filterData])];
        return $response;
    }

    /**
     * Notes: 异步处理数据项
     * Author: Uncle-L
     * Date: 2020/7/8
     * Time: 13:43
     * @param $data
     * @return mixed
     */
    protected function async_items(array $data) {
        $this->execOptionsCallbale("items");
        if (!$this->itemsCallback) throw_error_json("YunjTable [items] 未设置");
        $filterData = $data['filter'];
        $this->async_filterValidate($filterData, "items");
        $response = ['items' => call_user_func_array($this->itemsCallback, [$data['limit_start'], $data['limit_length'], $filterData, $data['sort']])];
        return $response;
    }

    /**
     * Notes: 异步处理触发事件
     * Author: Uncle-L
     * Date: 2020/7/8
     * Time: 13:43
     * @param $data
     * @return mixed
     */
    protected function async_event(array $data) {
        $response = call_user_func_array($this->eventCallback, [$data['filter']['event'], $data['filter']['ids']]);
        return $response;
    }

    /**
     * Notes: 异步处理数据导出
     * Author: Uncle-L
     * Date: 2020/7/8
     * Time: 13:43
     * @param $data
     * @return mixed
     */
    protected function async_export(array $data) {
        if (is_mobile()) return error_json('抱歉！暂不支持移动端导出数据');
        $this->execOptionsCallbale("items");
        if (!$this->itemsCallback) throw_error_json("YunjTable [items] 未设置");
        $filterData = $data['filter'];
        $this->async_filterValidate($filterData, "export");
        $response = [
            'page' => $this->page,
            'num' => $data['num'],
            'limit' => self::EXPORT_LIMIT,
            'items' => call_user_func_array($this->itemsCallback, [$data['limit_start'], $data['limit_length'], $filterData, $data['sort']])];
        return $response;
    }

    /**
     * Notes: 异步处理筛选表单数据提交验证
     * Author: Uncle-L
     * Date: 2021/10/20
     * Time: 16:34
     * @param array $data [验证数据]
     * @param string $scene [验证环境]
     * @return array
     */
    private function async_filterValidate(array &$data, string $scene): array {
        $this->execOptionsCallbale('state,filter,filterValidate');

        // state
        if ($this->isSetState()) {
            if (!isset($data["state"])) throw_error_json("filter[state]参数缺失");
            if (!isset($this->state[$data["state"]])) throw_error_json("filter[state]参数错误");
        }

        // filter
        if ($this->isSetFilter()) {
            $filter = $this->filter;
            if($this->isSetState()){
                $state = $data["state"];
                if(!isset($filter[$state])||!$filter[$state]) return $data;
                $fields = $filter[$state];
            }else{
                $fields = $filter;
            }

            $validate = $this->filterValidateCallback ? call_user_func($this->filterValidateCallback) : (new Validate());
            if (is_string($validate)) $validate = class_exists($validate) ? (new $validate()) : null;
            if (!$validate || !($validate instanceof Validate)) throw_error_json("YunjTable [filterValidate] 需返回 \\yunj\\Validate及其子类 的实例对象或全限定类名");

            $res = form_data_validate($validate, $fields, $data, $scene);
            if (!$res) throw_error_json($validate->getError());
        }

        return $data;
    }

}